<template>
  <div class="max-w-3xl mx-auto p-6">
    <div class="flex items-center justify-between mb-4">
      <h1 class="text-2xl font-semibold">智能对话</h1>
    </div>
    
    <div class="mb-3">
      <label class="text-sm text-gray-600">八八智能KEY</label>
      <div class="flex gap-2 mt-1">
        <input v-model="randomKey" placeholder="请输入八八智能KEY" class="flex-1 border rounded-md p-2" />
        <button type="button" @click="saveKey" class="px-3 py-2 bg-green-500 text-white rounded-md">保存</button>
        <button type="button" @click="clearKey" class="px-3 py-2 bg-gray-200 text-gray-700 rounded-md">清除</button>
      </div>
    </div>      
    <div ref="chatBody" class="border rounded-lg p-4 h-[60vh] overflow-auto bg-white shadow-sm">
      <div v-for="(msg, idx) in messages" :key="idx" class="mb-4">
        <div v-if="msg.role === 'user'" class="flex justify-end">
          <div class="max-w-[80%] bg-blue-100 text-right text-gray-800 px-4 py-2 rounded-lg break-words">
            <div v-html="parseMarkdown(msg.content)"></div>
          </div>
        </div>
        <div v-else class="flex justify-start">
          <div class="max-w-[80%] bg-gray-50 text-left text-gray-800 px-4 py-2 rounded-lg break-words">
            <div v-html="parseMarkdown(msg.content)"></div>
          </div>
        </div>
      </div>
    </div>

    <div class="mt-4">

      <form @submit.prevent="sendMessage" class="flex gap-2">
        <textarea v-model="inputMessage" @keydown.enter.exact.prevent="sendMessage" @keydown.enter.shift.stop class="flex-1 border rounded-md p-3 resize-none h-24" placeholder="请输入你的问题，Shift+Enter 换行"></textarea>
        <div class="flex flex-col items-end gap-2">
          <button type="submit" :disabled="isLoading || inputMessage.trim() === ''" class="px-4 py-2 bg-blue-500 text-white rounded-md disabled:opacity-50">{{ isLoading ? '生成中...' : '发送' }}</button>
          <button type="button" @click="clearConversation" class="text-sm text-gray-500">清空</button>
        </div>
      </form>
    </div>
  </div>
</template>

<script setup>
definePageMeta({ middleware: 'admin-access' })
import { ref, reactive, onMounted } from 'vue'
import { marked } from 'marked'

// Disable deprecated defaults in marked to avoid runtime warnings
// See: marked() deprecation warnings for mangle/headerIds in v5
marked.setOptions({ mangle: false, headerIds: false })
import { useRoute } from 'vue-router'

// 初始 system 提示（保留用户提供的内容）
const initialSystem = `你好，我是八八测算助手，提供八字合婚,流年运势,宝宝/公司起名,姓名配对,八字精批,紫薇斗数，十年大运，星座运势等在线测算，今年是${new Date().getFullYear()}年，我会根据您提供的信息进行详细的推算和分析并且给出一些建议，有什么需要我的帮助吗？`
//const initialSystem = `你好，我是88助手，今年是${new Date().getFullYear()}年，有什么需要我的帮助吗？`

const route = useRoute()
const chatBody = ref(null)
const inputMessage = ref('')
const isLoading = ref(false)
const randomKey = ref('')

const messages = reactive([{ role: 'system', content: initialSystem }])

function scrollToBottom() {
  setTimeout(() => {
    const el = chatBody.value
    if (el) el.scrollTop = el.scrollHeight
  }, 50)
}

function parseMarkdown(content) {
  try {
    return marked.parse(content || '')
  } catch (e) {
    return content
  }
}

function clearConversation() {
  messages.splice(0, messages.length)
  messages.push({ role: 'system', content: initialSystem })
}

async function sendMessage() {
  if (inputMessage.value.trim() === '') return
  const str = inputMessage.value
  inputMessage.value = ''
  messages.push({ role: 'user', content: str })
  scrollToBottom()

  isLoading.value = true
  try {
    await getAiResponse()
  } finally {
    isLoading.value = false
    scrollToBottom()
  }
}

async function getAiResponse() {
  try {
  // 添加一个空的 system 消息，用来接收流式响应
    messages.push({ role: 'system', content: '' })

    // 获取引用到最后一个 system 消息（上面刚添加的）
    const lastSystemIndex = [...messages].map((m, i) => ({ m, i })).reverse().find(item => item.m.role === 'system')?.i
    const lastSystemMessage = typeof lastSystemIndex === 'number' ? messages[lastSystemIndex] : null

  // 发送请求到本地代理接口（已由 server/api/chatstream2.post.ts 处理转发）
  const url = `/api/chatstream2`

    // 这个接口返回 SSE（text/event-stream）。常见做法是以 application/json 发起 POST，
    // 并在 Accept 中声明 text/event-stream，然后按 SSE 协议解析流。
    async function doPost(bodyPayload) {
      const controller = new AbortController()
      return fetch(url, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Accept': 'text/event-stream, application/json, text/plain',
        },
        body: JSON.stringify(bodyPayload),
        signal: controller.signal,
      })
    }

    // 准备要发送的请求体：优先发送 object 包含 messages，以便附带 randomKey
    const bodyPayload = (randomKey.value && randomKey.value.trim()) ? { messages, randomKey: randomKey.value.trim() } : messages
    let resp = await doPost(bodyPayload)

    // 如果服务端拒绝（415），再尝试以 { messageDtos } 包装发送（后端提示需要 messageDtos 字段）
    if (resp.status === 415) {
      const fallback = randomKey.value && randomKey.value.trim() ? { messageDtos: messages, randomKey: randomKey.value.trim() } : { messageDtos: messages }
      resp = await doPost(fallback)
    }

    if (!resp.body) {
      // 如果不是流式响应，直接解析完整文本
      const json = await resp.json().catch(() => null)
      if (json && json.content) {
        if (lastSystemMessage) lastSystemMessage.content += json.content
      } else {
        const text = await resp.text()
        if (lastSystemMessage) lastSystemMessage.content += text
      }
      return
    }

    const reader = resp.body.getReader()
    const decoder = new TextDecoder()
    let buffer = ''

    while (true) {
      const { done, value } = await reader.read()
      if (done) break
      const chunk = decoder.decode(value, { stream: true })
      buffer += chunk

      // 以换行分割（server 可能按行发送 data: ...）
      const lines = buffer.split('\n')
      buffer = lines.pop() || ''

      for (const line of lines) {
        const trimmed = line.trim()
        if (trimmed === '' || trimmed === 'data: [DONE]') continue
        const contentPart = trimmed.startsWith('data: ') ? trimmed.slice(6) : trimmed
        try {
          const data = JSON.parse(contentPart)
          if (data.choices && data.choices[0] && data.choices[0].delta && data.choices[0].delta.content) {
            const newContent = data.choices[0].delta.content
            if (lastSystemMessage) {
              lastSystemMessage.content += newContent
            }
            scrollToBottom()
          } else if (data.content) {
            if (lastSystemMessage) lastSystemMessage.content += data.content
            scrollToBottom()
          }
        } catch (err) {
          // 如果不是 JSON，直接追加文本
          if (lastSystemMessage) lastSystemMessage.content += contentPart
          scrollToBottom()
        }
      }
    }
  } catch (error) {
    console.error('getAiResponse error', error)
    messages.push({ role: 'system', content: '系统繁忙，请稍后重试或输入继续以继续对话。' })
    inputMessage.value = '继续'
  }
}

onMounted(() => {
  // 读取本地保存的八八智能 KEY
  try {
    const v = localStorage.getItem('bbe_random_key')
    if (v) randomKey.value = v
  } catch (e) { /* ignore */ }

  // 处理 route query（data_id / user_input）
  const userInput = route.query.user_input
  const dataId = route.query.data_id
  if (dataId) {
    // 尝试从本地工具加载数据（兼容旧代码）
    import('@/lin/util/data-transfer').then(({ getData, removeData }) => {
      const data = getData(dataId)
      if (data) {
        messages.push({ role: 'user', content: data })
        isLoading.value = true
        getAiResponse().finally(() => { isLoading.value = false })
        removeData(dataId)
      }
    }).catch((e) => {
      console.warn('读取 data-transfer 失败', e)
    })
  } else if (userInput) {
    messages.push({ role: 'user', content: userInput })
    isLoading.value = true
    getAiResponse().finally(() => { isLoading.value = false })
  }
})

function saveKey() {
  try {
    if (randomKey.value && randomKey.value.trim()) {
      localStorage.setItem('bbe_random_key', randomKey.value.trim())
    }
  } catch (e) { console.warn('保存KEY失败', e) }
}

function clearKey() {
  try {
    localStorage.removeItem('bbe_random_key')
    randomKey.value = ''
  } catch (e) { console.warn('清除KEY失败', e) }
}
</script>


